/**
 * xmlmodelconnector.cpp is part of %PROJECTNAME%.
 *
 * %PROJECTNAME% is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * %PROJECTNAME% is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with %PROJECTNAME%.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "xmlmodelconnector.h"
#include "../model.h"
#include "../definitions.h"
#include "../task.h"
#include "../resource.h"
#include "../constraints/tasktimewindowconstraint.h"
#include "../constraints/tasklenghtconstraint.h"
#include "../constraints/taskresourcesneededconstraint.h"
#include "../constraints/resourcecapacityconstraint.h"
#include "../constraints/modelallowidlesconstraint.h"
#include "../constraints/modelmaxtimeconstraint.h"
#include "../constraints/modelprecedenceconstraint.h"
#include "../factory/modelfactory.h"
#include "../factory/taskfactory.h"
#include "../factory/resourcefactory.h"
#include "../factory/taskconstraintfactory.h"
#include "../factory/resourceconstraintfactory.h"
#include "../factory/modelconstraintfactory.h"
#include "../../utils/logging/logmanager.h"
#include "../../utils/logging/logger.h"

const std::string XmlModelConnector::MODEL_TAG                               = "Model";
const std::string XmlModelConnector::TASK_TAG                               = "Task";
const std::string XmlModelConnector::RESOURCE_TAG                           = "Resource";
const std::string XmlModelConnector::CONSTRAINT_TAG                         = "Constraint";
const std::string XmlModelConnector::ID_ATTRIBUTE                           = "id";
const std::string XmlModelConnector::TYPE_ATTRIBUTE                         = "type";
const std::string XmlModelConnector::TASK_TIME_WINDOW_CONSTRAINT_TYPE       = "timewindow";
const std::string XmlModelConnector::START_ATTRIBUTE                        = "start";
const std::string XmlModelConnector::END_ATTRIBUTE                          = "end";
const std::string XmlModelConnector::TASK_LENGHT_CONSTRAINT_TYPE            = "lenght";
const std::string XmlModelConnector::LENGHT_ATTRIBUTE                       = "lenght";
const std::string XmlModelConnector::RESOURCE_CAPACITY_CONSTRAINT_TYPE      = "capacity";
const std::string XmlModelConnector::CAPACITY_ATTRIBUTE                     = "capacity";
const std::string XmlModelConnector::TASK_RESOURCES_NEEDED_CONSTRAINT_TYPE  = "resourcesneeded";
const std::string XmlModelConnector::RESOURCES_NEEDED_ATTRIBUTE             = "resourcesneeded" ;
const std::string XmlModelConnector::MODEL_ALLOW_IDLE_CONSTRAINT_TYPE       = "allowidle";
const std::string XmlModelConnector::ALLOW_IDLE_ATTRIBUTE                   = "allowidle";
const std::string XmlModelConnector::MODEL_MAX_TIME_CONSTRAINT_TYPE         = "maxtime";
const std::string XmlModelConnector::MAX_TIME_ATTRIBUTE                     = "maxtime";
const std::string XmlModelConnector::TRUE_STRING                            = "true";
const std::string XmlModelConnector::FALSE_STRING                           = "false";
const std::string XmlModelConnector::MODEL_PRECEDENCE_CONSTRAINT_TYPE       = "precedence";
const std::string XmlModelConnector::BEFORE_PRECEDENCE_ATTRIBUTE            = "before";
const std::string XmlModelConnector::AFTER_PRECEDENCE_ATTRIBUTE             = "after";

XmlModelConnector::XmlModelConnector( const std::string & a_file ) :
    XmlConnector( a_file )
{
}

XmlModelConnector::~XmlModelConnector()
{
    // Model cannot be destroyed.
    m_model = 0;
}

Model * XmlModelConnector::GetModel()
{
    return m_model;
}

void XmlModelConnector::ReadElement( QXmlStreamReader::TokenType a_type, QXmlStreamReader & a_xmlStream )
{
    if( IsStartElement( a_type ) )
    {
        std::string elementName = a_xmlStream.name().toString().toStdString() ;
        if( elementName == MODEL_TAG )
            ReadModel( a_xmlStream );
        if( elementName == TASK_TAG )
            ReadTask( a_xmlStream );
        else if( elementName == RESOURCE_TAG )
            ReadResource( a_xmlStream );
        else if( elementName == CONSTRAINT_TAG )
            ReadConstraint( a_xmlStream );
    }
}

void XmlModelConnector::ReadModel( QXmlStreamReader & a_xmlStream)
{
    QXmlStreamAttributes attributes = a_xmlStream.attributes();
    if( attributes.hasAttribute( ID_ATTRIBUTE.c_str() ) )
    {
        std::string modelId = attributes.value( ID_ATTRIBUTE.c_str() ).toString().toStdString();
        m_model = GET_MODEL_FACTORY().CreateModel( modelId );
    }
}

void XmlModelConnector::ReadTask( QXmlStreamReader & a_xmlStream )
{
    Task * newTask = 0;

    QXmlStreamAttributes attributes = a_xmlStream.attributes();
    if( attributes.hasAttribute( ID_ATTRIBUTE.c_str() ) )
    {
        std::string taskId = attributes.value( ID_ATTRIBUTE.c_str() ).toString().toStdString();
        newTask = GET_TASK_FACTORY().CreateTask( taskId );
    }

    if( m_model && newTask )
    {
        Logger * log = GET_LOG_MGR().GetLogger( "LOG" );
        if( log )
            log->LogInfo() << "Reading Task: " << newTask->TaskName();

        ReadTaskConstraints( a_xmlStream, newTask );
        m_model->Add( newTask );
    }
    else if( newTask )
    {
        delete newTask;
    }

    newTask = 0;
}

void XmlModelConnector::ReadTaskConstraints( QXmlStreamReader & a_xmlStream, Task * a_task )
{
    bool end = false;
    while( !end && !a_xmlStream.atEnd() )
    {
        QXmlStreamReader::TokenType tokenType = a_xmlStream.readNext();
        if( IsStartElement( tokenType ) )
        {
            std::string elementName = a_xmlStream.name().toString().toStdString() ;
            if( elementName == CONSTRAINT_TAG )
                ReadTaskConstraint( a_xmlStream, a_task );
            else
            {
                Logger * log = GET_LOG_MGR().GetLogger( "LOG" );
                if( log )
                    log->LogWarn() << TASK_TAG << " has unknown element : " << elementName;
            }
        }
        else if( IsEndElement( tokenType ) )
        {
            std::string elementName = a_xmlStream.name().toString().toStdString() ;
            end = elementName == TASK_TAG;
        }
    }
}

void XmlModelConnector::ReadTaskConstraint( QXmlStreamReader & a_xmlStream, Task * a_task )
{
    QXmlStreamAttributes xmlAttributes = a_xmlStream.attributes();
    if( xmlAttributes.hasAttribute( TYPE_ATTRIBUTE.c_str() ) )
    {
        std::string constraintType = xmlAttributes.value( TYPE_ATTRIBUTE.c_str() ).toString().toStdString();
        if( constraintType == TASK_TIME_WINDOW_CONSTRAINT_TYPE )
            ReadTaskTimeWindowConstraint( xmlAttributes, a_task );
        else if( constraintType == TASK_LENGHT_CONSTRAINT_TYPE )
            ReadTaskLenghtConstraint( xmlAttributes, a_task );
        else if( constraintType == TASK_RESOURCES_NEEDED_CONSTRAINT_TYPE )
            ReadTaskResourcesNeededConstraint( xmlAttributes, a_task );
        else
        {
            Logger * log = GET_LOG_MGR().GetLogger( "LOG" );
            if( log )
                log->LogWarn() << "Constraint type unknown: " << constraintType;
        }
    }
    else
    {
        Logger * log = GET_LOG_MGR().GetLogger( "LOG" );
        if( log )
            log->LogWarn() << " Task constraint without type";
    }
}

void XmlModelConnector::ReadTaskTimeWindowConstraint( QXmlStreamAttributes & a_xmlAttributes, Task * a_task )
{
    bool hasStart = a_xmlAttributes.hasAttribute( START_ATTRIBUTE.c_str() );
    bool hasEnd   = a_xmlAttributes.hasAttribute( END_ATTRIBUTE.c_str() );
    if( a_task && hasStart && hasEnd )
    {
        tTime start = a_xmlAttributes.value( START_ATTRIBUTE.c_str() ).toString().toUShort();
        tTime end   = a_xmlAttributes.value( END_ATTRIBUTE.c_str() ).toString().toUShort();
        a_task->Add( GET_TASK_CONSTRAINT_FACTORY().CreateTaskTimeWindowConstraint( start, end ) );
    }
    else
    {
        Logger * log = GET_LOG_MGR().GetLogger( "LOG" );
        if( log )
            log->LogWarn() << "Malformed TaskTimeWindowConstraint";
    }
}

void XmlModelConnector::ReadTaskLenghtConstraint( QXmlStreamAttributes & a_xmlAttributes, Task * a_task )
{
    bool hasLenght = a_xmlAttributes.hasAttribute( LENGHT_ATTRIBUTE.c_str() );
    if( a_task && hasLenght )
    {
        tTime lenght = a_xmlAttributes.value( LENGHT_ATTRIBUTE.c_str() ).toString().toUShort();
        a_task->Add( GET_TASK_CONSTRAINT_FACTORY().CreateTaskLenghtConstraint( lenght ) );
    }
    else
    {
        Logger * log = GET_LOG_MGR().GetLogger( "LOG" );
        if( log )
            log->LogWarn() << "Malformed TaskLenghtConstraint";
    }
}

void XmlModelConnector::ReadTaskResourcesNeededConstraint( QXmlStreamAttributes & a_xmlAttributes, Task * a_task )
{
    bool hasLenght = a_xmlAttributes.hasAttribute( RESOURCES_NEEDED_ATTRIBUTE.c_str() );
    if( a_task && hasLenght )
    {
        tNeededResources neededResources = a_xmlAttributes.value( RESOURCES_NEEDED_ATTRIBUTE.c_str() ).toString().toUShort();
        a_task->Add( GET_TASK_CONSTRAINT_FACTORY().CreateTaskResourcesNeededConstraint( neededResources ) );
    }
    else
    {
        Logger * log = GET_LOG_MGR().GetLogger( "LOG" );
        if( log )
            log->LogWarn() << "Malformed TaskResourcesNeededConstraint";
    }
}

void XmlModelConnector::ReadResource( QXmlStreamReader & a_xmlStream )
{
    Resource * newResource = 0;

    QXmlStreamAttributes attributes = a_xmlStream.attributes();
    if( attributes.hasAttribute( ID_ATTRIBUTE.c_str() ) )
    {
        std::string resourceId = attributes.value( ID_ATTRIBUTE.c_str() ).toString().toStdString();
        newResource = GET_RESOURCE_FACTORY().CreateResource( resourceId );
    }

    if( m_model && newResource )
    {
       Logger * log = GET_LOG_MGR().GetLogger( "LOG" );
       if( log )
           log->LogInfo() << "Reading Resource : " << newResource->ResourceName();

       ReadResourceConstraints( a_xmlStream, newResource );
       m_model->Add( newResource );
    }
    else if( newResource )
    {
        delete newResource;
    }

    newResource = 0;
}

void XmlModelConnector::ReadResourceConstraints( QXmlStreamReader & a_xmlStream, Resource * a_resource )
{
    bool end = false;
    while( !end && !a_xmlStream.atEnd() )
    {
        QXmlStreamReader::TokenType tokenType = a_xmlStream.readNext();
        if( IsStartElement( tokenType ) )
        {
            std::string elementName = a_xmlStream.name().toString().toStdString() ;
            if( elementName == CONSTRAINT_TAG )
                ReadResourceConstraint( a_xmlStream, a_resource );
            else
            {
                Logger * log = GET_LOG_MGR().GetLogger( "LOG" );
                if( log )
                    log->LogWarn() << RESOURCE_TAG << " has unknown element : " << elementName;
            }
        }
        else if( IsEndElement( tokenType ) )
        {
            std::string elementName = a_xmlStream.name().toString().toStdString() ;
            end = elementName == RESOURCE_TAG;
        }
    }
}

void XmlModelConnector::ReadResourceConstraint( QXmlStreamReader & a_xmlStream, Resource * a_resource )
{
    QXmlStreamAttributes xmlAttributes = a_xmlStream.attributes();
    if( xmlAttributes.hasAttribute( TYPE_ATTRIBUTE.c_str() ) )
    {
        std::string constraintType = xmlAttributes.value( TYPE_ATTRIBUTE.c_str() ).toString().toStdString();
        if( constraintType == RESOURCE_CAPACITY_CONSTRAINT_TYPE )
            ReadResourceCapacityConstraint( xmlAttributes, a_resource );
        else
        {
            Logger * log = GET_LOG_MGR().GetLogger( "LOG" );
            if( log )
                log->LogWarn() << "Constraint type unknown: " << constraintType;
        }
    }
    else
    {
        Logger * log = GET_LOG_MGR().GetLogger( "LOG" );
        if( log )
            log->LogWarn() << "Resource constraint without type";
    }
}

void XmlModelConnector::ReadResourceCapacityConstraint( QXmlStreamAttributes & a_xmlAttributes, Resource * a_resource )
{
    bool hasCapacity = a_xmlAttributes.hasAttribute( CAPACITY_ATTRIBUTE.c_str() );
    if( a_resource && hasCapacity )
    {
        tCapacity capacity = a_xmlAttributes.value( CAPACITY_ATTRIBUTE.c_str() ).toString().toUShort();
        a_resource->Add( GET_RESOURCE_CONSTRAINT_FACTORY().CreateResourceCapacityConstraint( capacity ) );
    }
    else
    {
        Logger * log = GET_LOG_MGR().GetLogger( "LOG" );
        if( log )
            log->LogWarn() << "Malformed TaskResourceCapacityConstraint";
    }
}

void XmlModelConnector::ReadConstraint(QXmlStreamReader & a_xmlStream )
{
    QXmlStreamAttributes xmlAttributes = a_xmlStream.attributes();
    if( xmlAttributes.hasAttribute( TYPE_ATTRIBUTE.c_str() ) )
    {
        std::string constraintType = xmlAttributes.value( TYPE_ATTRIBUTE.c_str() ).toString().toStdString();
        if( constraintType == MODEL_ALLOW_IDLE_CONSTRAINT_TYPE )
            ReadModelAllowIdleConstraint( xmlAttributes );
        else if( constraintType == MODEL_MAX_TIME_CONSTRAINT_TYPE )
            ReadModelMaxTimeConstraint( xmlAttributes );
        else if( constraintType == MODEL_PRECEDENCE_CONSTRAINT_TYPE )
            ReadModelPrecedenceConstraint( xmlAttributes );
        else
        {
            Logger * log = GET_LOG_MGR().GetLogger( "LOG" );
            if( log )
                log->LogWarn() << "Constraint type unknown: " << constraintType;
        }
    }
    else
    {
        Logger * log = GET_LOG_MGR().GetLogger( "LOG" );
        if( log )
            log->LogWarn() << "Model constraint without type";
    }
}

void XmlModelConnector::ReadModelAllowIdleConstraint( QXmlStreamAttributes & a_xmlAttributes )
{
    bool hasAllowIdle = a_xmlAttributes.hasAttribute( ALLOW_IDLE_ATTRIBUTE.c_str() );
    if( m_model && hasAllowIdle )
    {
        std::string allowIdleStr = a_xmlAttributes.value( ALLOW_IDLE_ATTRIBUTE.c_str() ).toString().toStdString();
        bool allowIdle = allowIdleStr == TRUE_STRING ? true : false;
        m_model->Add( GET_MODEL_CONSTRAINT_FACTORY().CreateAllowIdleConstraint( allowIdle ) );
    }
    else
    {
        Logger * log = GET_LOG_MGR().GetLogger( "LOG" );
        if( log )
            log->LogWarn() << "Malformed ModelAllowIdleConstraint";
    }
}

void XmlModelConnector::ReadModelMaxTimeConstraint( QXmlStreamAttributes & a_xmlAttributes )
{
    bool hasAllowIdle = a_xmlAttributes.hasAttribute( MAX_TIME_ATTRIBUTE.c_str() );
    if( m_model && hasAllowIdle )
    {
        tTime maxTime = a_xmlAttributes.value( MAX_TIME_ATTRIBUTE.c_str() ).toString().toUShort();
        m_model->Add( GET_MODEL_CONSTRAINT_FACTORY().CreateMaxTimeConstraint( maxTime ) );
    }
    else
    {
        Logger * log = GET_LOG_MGR().GetLogger( "LOG" );
        if( log )
            log->LogWarn() << "Malformed ModelMaxTimeConstraint";
    }
}

void XmlModelConnector::ReadModelPrecedenceConstraint( QXmlStreamAttributes & a_xmlAttributes )
{
    bool hasBefore = a_xmlAttributes.hasAttribute( BEFORE_PRECEDENCE_ATTRIBUTE.c_str() );
    bool hasAfter  = a_xmlAttributes.hasAttribute( AFTER_PRECEDENCE_ATTRIBUTE.c_str() );
    if( m_model && hasBefore && hasAfter )
    {
        std::string taskBefore = a_xmlAttributes.value( BEFORE_PRECEDENCE_ATTRIBUTE.c_str() ).toString().toStdString();
        std::string taskAfter  = a_xmlAttributes.value( AFTER_PRECEDENCE_ATTRIBUTE.c_str() ).toString().toStdString();
        m_model->Add( GET_MODEL_CONSTRAINT_FACTORY().CreatePrecedenceConstraint( taskBefore, taskAfter ) );
    }
    else
    {
        Logger * log = GET_LOG_MGR().GetLogger( "LOG" );
        if( log )
            log->LogWarn() << "Malformed ModelMaxTimeConstraint";
    }
}

void XmlModelConnector::HandleError( QXmlStreamReader & a_xmlStream )
{

}
